<!-- 将API不友好的El-Tree-Transfer 封装成支持v-modle的组件-->
<template>
  <div>
    <tree-transfer
      :from_data="fromData"
      :to_data="toData"
      :defaultProps="{ label: 'label' }"
      @add-btn="add"
      @remove-btn="remove"
      :mode="'transfer'"
      v-bind="$props"
      filter
      openAll
    >
    </tree-transfer>
  </div>
</template>

<script>
import { cloneDeep } from "lodash";
import treeTransfer from "el-tree-transfer";
export default {
  name: "MyTreeTransfer",
  components: { treeTransfer },
  props: {
    value: {
      type: Array,
      default: () => [],
    },
    treeData: {
      type: Array,
      default: () => [],
    },
  },
  watch: {
    value(val, oldVal) {
      if (val === oldVal) {
        return;
      }
      this.buildData();
    },
    treeData() {
      this.buildData();
    },
  },
  mounted() {
    this.buildData();
  },
  data() {
    return {
      fromData: [],
      toData: [],
    };
  },
  methods: {
    buildData() {
      const values = this.value;
      const treeNodes = this.treeData;
      treeDataValidation(treeNodes);
      const pathArray = buildToDataPathArray(treeNodes, values);
      const toData = buildToData(pathArray);
      const fromData = buildFromData(treeNodes, values);
      this.fromData = fromData;
      this.toData = toData;
    },
    emitValue(toData) {
      const values = [];
      function dfs(nodes) {
        nodes.forEach((node) => {
          if (!node.children || node.children.length === 0) {
            values.push(node.id);
          } else {
            dfs(node.children);
          }
        });
      }
      dfs(toData);

      this.$emit("input", values);
      this.$emit("change", values);
      return values;
    },
    // 监听穿梭框组件添加
    add(fromData, toData) {
      this.emitValue(toData);
    },
    // 监听穿梭框组件移除
    remove(fromData, toData) {
      this.emitValue(toData);
    },
  },
};

function treeDataValidation(treeNodes){
  function dfs(treeNodes, depth) {
    treeNodes.forEach(treeNode => {
      if (depth > 0 && !treeNode.pid) {
        throw Error("treeData节点必须要有pid" + JSON.stringify(treeNode))
      }
      if (treeNode.children) {
        dfs(treeNode.children, depth + 1);
      }
    })
  }
  dfs(treeNodes, 0)
}
function buildFromData(treeNodes, selectedValues) {
  function removeNode(fromData, value) {
    if (!fromData) {
      return;
    }
    const emptyParentNodes = [];
    for (let i = 0; i < fromData.length; i++) {
      if (fromData[i].id === value) {
        fromData.splice(i, 1);
      } else if (fromData[i].children) {
        removeNode(fromData[i].children, value);
        if (fromData[i].children.length === 0) {
          emptyParentNodes.push(fromData[i].id);
        }
      }
    }
    emptyParentNodes.forEach((parentNodeId) => {
      for (let i = 0; i < fromData.length; i++) {
        if (parentNodeId === fromData[i].id) {
            fromData.splice(i, 1);
        }
      }
    });
  }
  const fromData = cloneDeep(treeNodes);

  selectedValues.forEach((value) => {
    removeNode(fromData, value);
  });
  return fromData;
}
function buildToData(pathArray) {
  function createNode(path, toData, depth) {
    if (depth < path.length) {
      let exists = toData.find((t) => t.id === path[depth].id);
      if (!exists) {
        exists = cloneDeep(path[depth]);
        exists.children = [];
        toData.push(exists);
      }
      createNode(path, exists.children, depth + 1);
    }
  }

  const result = [];
  pathArray.forEach((path) => {
    createNode(path, result, 0);
  });
  return result;
}
function buildToDataPathArray(treeNodes, selectedValues) {
  const toDataPath = [];

  const valuePath = [];
  function dfsBuildToDataPathArray(treeNodes, id) {
    for (let i = 0; i < treeNodes.length; i++) {
      valuePath.push(treeNodes[i]);
      if (treeNodes[i].id === id) {
        //找到选择的节点， 将节点路径添加到结果集中
        const node = cloneDeep(valuePath);
        node.children = [];
        toDataPath.push(node);
      } else if (treeNodes[i].children) {
        dfsBuildToDataPathArray(treeNodes[i].children, id);
      }
      valuePath.pop();
    }
  }

  selectedValues.forEach((v) => {
    dfsBuildToDataPathArray(treeNodes, v);
  });

  return toDataPath;
}
</script>